﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Demo.EventBus
{
    /// <summary>
    /// EventBus实现
    /// </summary>
    public static class EventBus
    {
        // 订阅者字典，键为事件类型，值为处理事件的方法列表
        private static readonly Dictionary<string, List<Action<object>>> Subscribers = new Dictionary<string, List<Action<object>>>();

        // 事件队列，用于存放待处理的事件
        private static readonly BlockingCollection<Event> EventQueue = new BlockingCollection<Event>();

        // 用于取消事件处理任务的令牌
        private static CancellationTokenSource _cancellationTokenSource;

        // 处理事件的任务
        private static Task _eventProcessingTask;

        // 事件类，封装事件类型和数据
        private class Event
        {
            public string EventType { get; }
            public object EventData { get; }

            public Event(string eventType, object eventData)
            {
                EventType = eventType;
                EventData = eventData;
            }
        }

        /// <summary>
        /// 启动方法
        /// </summary>
        public static void Start()
        {
            // 初始化取消令牌和事件处理任务
            _cancellationTokenSource = new CancellationTokenSource();
            _eventProcessingTask = Task.Run(() => ProcessEvents(), _cancellationTokenSource.Token);

            // 扫描并注册所有实现了 IEventSubscriber 接口的类作为订阅者
            var subscriberTypes = Assembly.GetExecutingAssembly().GetTypes()
                .Where(t => typeof(IEventSubscriber).IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract);

            foreach (var subscriberType in subscriberTypes)
            {
                var instance = Activator.CreateInstance(subscriberType);
                Register(instance); // 注册订阅者
            }
        }

        /// <summary>
        /// 注册订阅者
        /// </summary>
        public static void Stop()
        {
            // 取消事件处理任务
            _cancellationTokenSource.Cancel();
            EventQueue.CompleteAdding();
            try
            {
                _eventProcessingTask.Wait(); // 等待任务完成
            }
            catch (AggregateException ex)
            {
                ex.Handle(e => e is OperationCanceledException);
            }
        }

        /// <summary>
        /// 注册订阅者
        /// </summary>
        /// <param name="subscriber"></param>
        private static void Register(object subscriber)
        {
            // 获取订阅者对象的所有方法
            var methods = subscriber.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
            foreach (var method in methods)
            {
                // 查找方法上的 EventSubscribeAttribute 特性
                var attributes = method.GetCustomAttributes(typeof(EventSubscribeAttribute), false);
                foreach (EventSubscribeAttribute attribute in attributes)
                {
                    // 根据事件类型将方法包装为 Action<object> 委托，并加入到订阅者字典中
                    if (!Subscribers.ContainsKey(attribute.EventType))
                    {
                        Subscribers[attribute.EventType] = new List<Action<object>>();
                    }
                    Subscribers[attribute.EventType].Add(eventData => method.Invoke(subscriber, new[] { eventData }));
                }
            }
        }

        /// <summary>
        /// 注销订阅者
        /// </summary>
        /// <param name="subscriber"></param>
        private static void Unregister(object subscriber)
        {
            var methods = subscriber.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
            foreach (var method in methods)
            {
                var attributes = method.GetCustomAttributes(typeof(EventSubscribeAttribute), false);
                foreach (EventSubscribeAttribute attribute in attributes)
                {
                    if (Subscribers.ContainsKey(attribute.EventType))
                    {
                        // 移除订阅者字典中对应事件类型的方法
                        Subscribers[attribute.EventType] = Subscribers[attribute.EventType]
                            .Where(handler => handler.Target != subscriber || handler.Method != method)
                            .ToList();

                        // 如果事件类型对应的方法列表为空，则从订阅者字典中移除该事件类型
                        if (Subscribers[attribute.EventType].Count == 0)
                        {
                            Subscribers.Remove(attribute.EventType);
                        }
                    }
                }
            }
        }

        /// <summary>
        /// 发布事件
        /// </summary>
        /// <param name="eventType"></param>
        /// <param name="eventData"></param>
        public static void Publish(string eventType, object eventData)
        {
            EventQueue.Add(new Event(eventType, eventData)); // 将事件加入事件队列
        }

        /// <summary>
        /// 事件处理方法
        /// </summary>
        private static void ProcessEvents()
        {
            //BlockingCollection.GetConsumingEnumerable 此方法会阻塞当前线程，直到队列中有元素可消费
            foreach (var eventInstance in EventQueue.GetConsumingEnumerable(_cancellationTokenSource.Token))
            {
                if (Subscribers.ContainsKey(eventInstance.EventType))
                {
                    // 遍历事件类型对应的所有订阅者方法，并调用处理事件数据
                    foreach (var handler in Subscribers[eventInstance.EventType])
                    {
                        try
                        {
                            handler(eventInstance.EventData);
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine($"Error handling event: {ex.Message}");
                        }
                    }
                }
            }
        }
    }

    /// <summary>
    /// 此接口用于标识事件订阅者类
    /// </summary>
    public class IEventSubscriber
    {
    }

    /// <summary>
    /// 用于标识事件订阅者的特性
    /// </summary>
    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
    public class EventSubscribeAttribute : Attribute
    {
        public string EventType { get; }

        public EventSubscribeAttribute(string eventType)
        {
            EventType = eventType;
        }
    }
}
